!pr1
Shortening the DOS File Buffer Builder.....Bob Sander-Cederlof

Lately I have been looking through DOS for subroutines that can be shrunk.  There seem to be a lot of them, or at least I have been lucky in finding some easy ones with little trouble.  Elsewhere this month I show how to shrink the numeric input conversion routine, saving enough bytes to make room for a useful new feature.

Yesterday I happened across the file buffer initializer, which starts at $A7D4 and goes up to $A850.  Scanning quickly through the code it looked a likely candidate for the shrinking process.  If you take a quick peek, you'll see that it starts out with an SEC instruction that is totally unnecessary.  Already we have shaved off one byte!

The DOS file buffers are each 595 bytes, linked together with a chain of pointers.  There are normally three buffers, starting at $9600, $9853, and $9AA6.  (If you have "Beneath Apple DOS", look on page 6-13 for some explanation.)  Each buffer contains a 256 byte area for data, another 256 byte area for a track/ sector list, a 30-character filename, a 45-byte working area for the DOS File Manager, and 4 2-byte pointers.  There is a two-byte pointer kept at $9D00,9D01 which points at the first character of the filename in the highest buffer.  This is normally $9CD3.  Here is a picture of the normal three buffers, all chained together:


9D00:  9CD3


 9CF7- Link addr              Link addr              Link addr
        ($9A80)                ($982D)                ($0000)
 9CF5- Data addr              Data addr              Data addr
        ($9AA6)                ($9853)                ($9600)
 9CF3- TSL addr               TSL addr               TSL addr
        ($9BA6)                ($9953)                ($9700)
 9CF1- FMW addr               FMW addr               FMW addr
        ($9CA6)                ($9A53)                ($9800)

       30-chars
 9CD3- filename         9A80- filename         982D- filename


       45 bytes
 9CA6- FMW area         9A53- FMW area         9800- FMW area


       256 bytes
 9BA6- TSL area         9953- TSL area         9700- TSL area


       256 bytes
 9AA6- Data area        9853- Data area        9600- Data area
!np
The file buffer initializer gets called during the boot procedure, and by the MAXFILES command processor.  There are two input parameters:  the start of buffers address at $9D00, and the number of file buffers at $AA57.  The job of the initializer is to fill in the four address values at the top of each buffer, to store a 00 byte in the first character of the filename of each buffer, and to store a new value in the HIMEM variable for the current language.  Here's the way it was, without comments.










































I rearranged the code, kept mental track of carry status, optimized register usage, and lopped off 11 bytes.  Speed is no issue, because it is not a time critical operation anyway, but mine may be a tad quicker.  Compare the two versions, and you can learn a few tricks for your own use.
!np










I found it even more interesting to re-write this program using the 65802 capabilities.  The 16-bit registers save a lot of byte shuffling, and eliminate the need for TEMP and PNTR.  What's more, instead of saving only 11 bytes over the original DOS 3.3 version, this time I whacked out 46 bytes!  And it could be made even smaller, if we could make some assumptions about the CPU status.

In general, we don't know whether we are in 65802 or 6502 mode until we peek at the "hidden" status bit (the E-bit).  In the process of peeking we may change it, and may also change the M- and X-bits.  Lines 1190 save the current status, flip into '802 mode and save the status again.  The first PHP is there in case we were already in '802 mode.  If we were, it saves the M- and X- bits and they will be restored by the PLP at line 1620.  The second PHP saves the status of the mysterious E-bit (the XCE opcode swaps E and C).  Lines 1600-1610 pull this saved status and do another XCE, restoring E to what it was when this sub- routine was called.  If we could ASSUME that we were called in '802 mode, we could delete lines 1190-1210 and lines 1610-1620 (saving 5 more bytes).  Or, if we could be sure we were always called from 6502 mode, we could delete 1190, 1220, and 1620, and change line 1600 to ".4  SEC" (saving 3 bytes).  Probably better never to assume, at least until we are a lot more familiar with this marvelous chip.

The XCE instruction swaps the C- and E-bits, but that is not necessarily all.  The M- and X-bits always come up in the 8-bit mode after an XCE.  Therefore in line 1240, the LDX will load $00 into the high byte of the X-register and the number of buffers into the low byte.  In line 1250 I turn on 16-bit mode for both indexing and memory-accumulator operations, and I will keep it that way until the PLP at line 1600.

6502 programs are always full of page zero pointer addressing modes, but in 65802 programs we may see a lot less of them.  Now we can load a whole 16-bit address into the X- or Y- register.

       Instead of:             We can write:
       -----------             -------------
       LDA BUF.PNTR
       STA PNTR
       LDA BUF.PNTR+1
       STA PNTR+1
       LDY #$1E                LDY BUF.PNTR
       LDA DATA...             LDA DATA...
       STA (PNTR),Y            STA $1E,Y


Lines 1280-1290 zero the first byte of the filename.  As an "extra" feature now, the second byte is also zeroed.  In lines 1300-1380 I can compute and store the three area pointers in a very straightforward manner.  It now occurs to me that by swapping the roles of the X- and Y-registers I could save six more bytes, since the STA $offset,X instructions would assemble in two bytes rather than three.  (The only problem might be that the D-register must = $0000 for this to work.)

Since I don't have to use the X-register to hold temporary values during the buffer creation loop, I can use it instead to count buffers.  Lines 1400-1410 do the counting.

If we have not just built the last buffer, lines 1420-1460 set the "next buffer" link address and branch back to build another buffer.

Lines 1470-1500 save the address of the data area in the X- register and store 0000 in the link address for the last buffer.  The data area address is going to be the new HIMEM value.

Lines 1510-1590 store the new HIMEM value for the currently selected language.  If we are in Applesoft, the string area normally bumps against HIMEM; we now empty that area, because HIMEM may have moved.  If we are in Integer BASIS or the S-C Assembler (which fools DOS into believing it is I/B), the source program nestles against HIMEM; it is therefore emptied by storing the HIMEM value into PP.

Won't it be nice when we all have 65802's and can USE these new code segments?  It may not be as long as you think.  In the mean time, maybe we can develop our expertise.  And we can carve enough holes in DOS to leave room for some great new features.
